/**
 * @file wa_obj.c
 *
 */

 /*********************
  *      INCLUDES
  *********************/
#include "wa_obj.h"
#include "wa_misc.h"
#include "wa_mask_draw.h"
#include "wa_wm.h"
#include "wa_malloc.h"
/*********************
*      DEFINES
*********************/

/**********************
*      TYPEDEFS
**********************/

/**********************
*  STATIC PROTOTYPES
**********************/
static void wa_obj_refresh_child_position(wa_obj* obj, int x_diff, int y_diff);
static void _obj_callback(wa_sign* sign);
static void top_scr_obj_callback(wa_sign* sign);
/**********************
*  STATIC VARIABLES
**********************/
wa_obj top_scr_obj;
/**********************
*      MACROS
**********************/

/**********************
*   GLOBAL FUNCTIONS
**********************/

/**
 * obj module init
 */
void wa_obj_module_init(void)
{
	top_scr_obj.par = NULL;
	top_scr_obj.rect.x1 = 0;
	top_scr_obj.rect.y1 = 0;
	top_scr_obj.rect.x2 = WA_LCD_W - 1;
	top_scr_obj.rect.y2 = WA_LCD_H - 1;
	top_scr_obj.flag = 0;
	top_scr_obj.flag |= WA_CF_SHOW | WA_CF_ENABLE;
	wa_obj_set_callback(&top_scr_obj, top_scr_obj_callback);
	wa_ll_init(&(top_scr_obj.child_ll), sizeof(wa_obj));
	wa_wm_update_redraw_rect(&(top_scr_obj.rect));
}

/**
 * create a obj
 * @param par parent obj
 * @param copy which obj will be copy
 * @return new obj
 */
wa_obj* wa_obj_create(wa_obj* par, wa_obj* copy)
{
	wa_obj* new_obj;
	if (par == NULL)
	{
		par = &top_scr_obj;
	}
	new_obj = wa_ll_add_head(&(par->child_ll));
	new_obj->par = par;
	wa_ll_init(&(new_obj->child_ll), sizeof(wa_obj));
	new_obj->rect.x1 = par->rect.x1;
	new_obj->rect.y1 = par->rect.y1;
	new_obj->rect.x2 = par->rect.x1 + WA_OBJ_DEF_W;
	new_obj->rect.y2 = par->rect.y1 + WA_OBJ_DEF_H;
	new_obj->ext_attr = NULL;
	new_obj->flag = 0;
	new_obj->flag |= WA_CF_SHOW | WA_CF_ENABLE;
	wa_obj_set_callback(new_obj, _obj_callback);

	if (copy != NULL)
	{
		wa_area_copy(&(new_obj->rect), &(copy->rect));
		wa_obj_set_pos(new_obj, wa_obj_get_rel_x(copy), wa_obj_get_rel_y(copy));
	}

	wa_wm_send_sign_nodata(par, WA_SIGNAL_CHILD_CHG);
	wa_wm_send_sign_nodata(new_obj, WA_SIGNAL_CREATE);

	wa_obj_invalidate(new_obj);
	return new_obj;
}

/**
 * delete a obj
 * @param obj obj
 */
void wa_obj_del(wa_obj* obj)
{
	wa_obj* child;
	wa_obj* child_next;
	wa_obj* par;

	wa_obj_invalidate(obj);

	child = wa_ll_get_head(&(obj->child_ll));
	while (child != NULL)
	{
		child_next = wa_ll_get_next(&(obj->child_ll), child);
		wa_obj_del(child);
		child = child_next;
	}

	par = wa_obj_get_parent(obj);
	wa_ll_remove(&(par->child_ll), obj);

	wa_wm_send_sign_nodata(obj, WA_SIGNAL_DELETE);

	if ((obj->ext_attr) != NULL)
	{
		wa_free(obj->ext_attr);
	}

	wa_free(obj);

	wa_wm_send_sign_nodata(par, WA_SIGNAL_CHILD_CHG);
}

/**
 * delete all child obj
 * @param obj obj
 */
void wa_obj_clean_child(wa_obj* obj)
{
	wa_obj* child;
	for (child = wa_obj_get_child_next(obj, NULL); child != NULL; child = wa_obj_get_child_next(obj, child))
	{
		wa_obj_del(child);
	}
}

/**
 * set obj's parent
 * @param obj obj
 * @param par obj's parent
 */
void wa_obj_set_parent(wa_obj* obj, wa_obj* par)
{
	int x_old, y_old;
	wa_obj* old_par;

	wa_obj_invalidate(obj);
	old_par = wa_obj_get_parent(obj);
	x_old = wa_obj_get_rel_x(obj);
	y_old = wa_obj_get_rel_y(obj);

	wa_ll_chg_list(&(old_par->child_ll), &(par->child_ll), obj);
	obj->par = par;
	wa_obj_set_pos(obj, x_old, y_old);

	wa_wm_send_sign_nodata(par, WA_SIGNAL_CHILD_CHG);
	wa_wm_send_sign_nodata(old_par, WA_SIGNAL_CHILD_CHG);

	wa_obj_invalidate(obj);
}

/**
 * get x relative parent obj
 * @param obj obj
 * @return x relative parent obj
 */
int wa_obj_get_rel_x(wa_obj* obj)
{
	int rel_x;
	wa_obj* par = wa_obj_get_parent(obj);
	rel_x = (obj->rect.x1) - (par->rect.x1);
	return rel_x;
}

/**
 * get y relative parent obj
 * @param obj obj
 * @return y relative parent obj
 */
int wa_obj_get_rel_y(wa_obj* obj)
{
	int rel_y;
	wa_obj* par = wa_obj_get_parent(obj);
	rel_y = (obj->rect.y1) - (par->rect.y1);
	return rel_y;
}

/**
 * get width of obj
 * @param obj obj
 * @return width
 */
int wa_obj_get_width(wa_obj* obj)
{
	int w;
	w = WA_MATH_ABS((obj->rect.x2) - ((obj->rect.x1) - 1));
	return w;
}

/**
 * get height of obj
 * @param obj obj
 * @return height
 */
int wa_obj_get_height(wa_obj* obj)
{
	int h;
	h = WA_MATH_ABS((obj->rect.y2) - ((obj->rect.y1) - 1));
	return h;
}

/**
 * set obj position relative parent obj
 * @param obj obj
 * @param x x relative parent obj
 * @param y y relative parent obj
 */
void wa_obj_set_pos(wa_obj* obj, int x, int y)
{
	wa_obj* par;
	int x_diff, y_diff;
	par = obj->par;
	x = x + (par->rect.x1);
	y = y + (par->rect.y1);
	x_diff = x - (obj->rect.x1);
	y_diff = y - (obj->rect.y1);
	wa_obj_invalidate(obj);
	obj->rect.x1 += x_diff;
	obj->rect.y1 += y_diff;
	obj->rect.x2 += x_diff;
	obj->rect.y2 += y_diff;
	wa_obj_refresh_child_position(obj, x_diff, y_diff);

	wa_wm_send_sign_nodata(obj, WA_SIGNAL_MOVE);
	wa_wm_send_sign_nodata(par, WA_SIGNAL_CHILD_CHG);

	wa_obj_invalidate(obj);
}

/**
 * set obj size
 * @param obj obj
 * @param w width
 * @param h height
 */
void wa_obj_set_size(wa_obj* obj, int w, int h)
{
	wa_obj* par;
	if (wa_obj_get_width(obj) == w && wa_obj_get_height(obj) == h)
	{
		return;
	}
	wa_obj_invalidate(obj);
	obj->rect.x2 = (obj->rect.x1) + w - 1;
	obj->rect.y2 = (obj->rect.y1) + h - 1;

	wa_wm_send_sign_nodata(obj, WA_SIGNAL_SIZE_CHG);

	par = wa_obj_get_parent(obj);
	wa_wm_send_sign_nodata(par, WA_SIGNAL_CHILD_CHG);

	wa_obj_invalidate(obj);
}
/**
 * set obj callback
 * @param obj
 * @param cb
 */
void wa_obj_set_callback(wa_obj* obj, wa_obj_callback cb)
{
	obj->cb = cb;
}

/**
 * obj invalidate
 * @param obj
 */
void wa_obj_invalidate(wa_obj* obj)
{
	wa_result union_ok = wa_nok;
	wa_area area_trunc;
	wa_obj* par;
	wa_area_copy(&area_trunc, &(obj->rect));
	par = wa_obj_get_parent(obj);
	while (par != NULL)
	{
		union_ok = wa_area_union(&area_trunc, &area_trunc, &(par->rect));
		if (union_ok == wa_nok)
		{
			break;
		}
		par = wa_obj_get_parent(par);
	}
	if (union_ok == wa_ok)
	{
		wa_wm_update_redraw_rect(&area_trunc);
	}
}

/**
 * obj part invalidate
 * @param obj
 */
void wa_obj_part_invalidate(wa_obj* obj, wa_area* part)
{
	wa_result union_ok = wa_nok;
	wa_area area_trunc;
	wa_obj* par;
	wa_area_copy(&area_trunc, &(obj->rect));
	if (wa_area_union(&area_trunc, &area_trunc, part) == wa_nok)
	{
		return;
	}
	par = wa_obj_get_parent(obj);
	while (par != NULL)
	{
		union_ok = wa_area_union(&area_trunc, &area_trunc, &(par->rect));
		if (union_ok == wa_nok)
		{
			break;
		}
		par = wa_obj_get_parent(par);
	}
	if (union_ok == wa_ok)
	{
		wa_wm_update_redraw_rect(&area_trunc);
	}
}

/**
 * get obj's parent
 * @param obj
 */
wa_obj* wa_obj_get_parent(wa_obj* obj)
{
	return obj->par;
}

/**
 * get next obj
 * @param obj 
 * @param child 
 * @return 
 */
wa_obj* wa_obj_get_child_next(wa_obj* obj, wa_obj* child)
{
	if (child == NULL)
	{
		return wa_ll_get_head(&(obj->child_ll));
	}
	else
	{
		return wa_ll_get_next(&(obj->child_ll), child);
	}
}

/**
 * get prev obj
 * @param obj 
 * @param child 
 * @return 
 */
wa_obj* wa_obj_get_child_prev(wa_obj* obj, wa_obj* child)
{
	if (child == NULL)
	{
		return wa_ll_get_tail(&(obj->child_ll));
	}
	else
	{
		return wa_ll_get_prev(&(obj->child_ll), child);
	}
}

/**
 * statistics child obj count
 * @param obj 
 * @return child obj count of obj
 */
uint16_t wa_obj_count_child(wa_obj* obj)
{
	wa_obj* child;
	uint16_t cnt = 0;

	for (child = wa_ll_get_head(&(obj->child_ll)); child != NULL; child = wa_ll_get_next(&(obj->child_ll), child))
	{
		cnt++;
	}

	return cnt;
}

/**
 * malloc mem to obj ext_attr
 * @param obj
 * @param ext_size
 * @return
 */
void* wa_obj_malloc_ext_attr(wa_obj* obj, uint16_t ext_size)
{
	obj->ext_attr = wa_realloc(obj->ext_attr, ext_size);
	return (void*)obj->ext_attr;
}

/**
 * get obj ext_attr
 * @param obj
 * @return
 */
void* wa_obj_get_ext_attr(wa_obj* obj)
{
	return obj->ext_attr;
}

/**
 * get obj callback
 * @param obj
 * @return 
 */
wa_obj_callback wa_obj_get_callback(wa_obj* obj)
{
	return obj->cb;
}

/**
 * obj to top
 * @param obj
 */
void wa_obj_to_top(wa_obj* obj)
{
	wa_obj* par;
	par = wa_obj_get_parent(obj);
	wa_ll_chg_list(&(par->child_ll), &(par->child_ll), (void*)obj);
	wa_wm_send_sign_nodata(par, WA_SIGNAL_CHILD_CHG);
	wa_obj_invalidate(obj);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

 /**
  * refresh child position
  * @param obj 
  * @param x_diff
  * @param y_diff
  */
static void wa_obj_refresh_child_position(wa_obj* obj, int x_diff, int y_diff)
{
	wa_obj* child;
	for (child = wa_ll_get_head(&(obj->child_ll)); child != NULL; child = wa_ll_get_next(&(obj->child_ll), child))
	{
		child->rect.x1 += x_diff;
		child->rect.y1 += y_diff;
		child->rect.x2 += x_diff;
		child->rect.y2 += y_diff;
		wa_obj_refresh_child_position(child, x_diff, y_diff);

		wa_wm_send_sign_nodata(child, WA_SIGNAL_MOVE);
		wa_wm_send_sign_nodata(obj, WA_SIGNAL_CHILD_CHG);
	}
}

static void _obj_callback(wa_sign* sign)
{
	int sign_id;
	sign_id = sign->sign_id;
	switch (sign_id)
	{
		case WA_SIGNAL_PAINT:
		{
			break;
		}
		default:
		{
			break;
		}
	}
}

static void top_scr_obj_callback(wa_sign* sign)
{
	int sign_id;
	wa_obj* obj;
	_obj_callback(sign);
	sign_id = sign->sign_id;
	obj = sign->obj_dst;
	switch (sign_id)
	{
		case WA_SIGNAL_PAINT:
		{
			wa_mask_fillrect(obj->rect.x1, obj->rect.y1,
				wa_obj_get_width(obj),
				wa_obj_get_height(obj),
				&(obj->redraw_rect),
				WA_COLOR_BACKGROUND);
			break;
		}
		default:
		{
			break;
		}
	}
}
